package com.renfei.medialibrary;


import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.view.Surface;

import com.renfei.medialibrary.opengl.OpenGLUtil;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class YuvRender implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
    private Context context;
    private static final String TAG = "YuvRender";

    public static final int RENDER_TYPE_YUV = 0;
    public static final int RENDER_TYPE_SURFACE = 1;

    private float[] vertexData = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f
    };

    //屏幕坐标
    private float[] textureData = {
            0f, 1f,
            1f, 1f,
            0f, 0f,
            1f, 0f
    };

    private FloatBuffer vertexBuffer;
    private FloatBuffer textureBuffer;


    private int renderType=RENDER_TYPE_YUV;

    //yuv渲染
    private int programYuv;
    private int av_Position_yuv;//attribute  vec4
    private int af_Position_yuv;  //attribute   vec2

    private int sampler_y;  //uniform sampler2D
    private int sampler_u;  //uniform sampler2D
    private int sampler_v;  //uniform sampler2D

    private int[] textureId_yuv;

    private int width_yuv;
    private int height_yuv;

    private ByteBuffer y;
    private ByteBuffer u;
    private ByteBuffer v;

    /**
     * 纹理渲染
     */
    private int program_mediacodec;
    private int av_Position_mediacodec;//attribute  vec4
    private int af_Position_mediacodec;  //attribute   vec2
    private int samplerOES_mediacodec;//uniform samplerExternalOES
    private int textureId_mediacodec;// 渲染的纹理ID
    private SurfaceTexture surfaceTexture;
    private Surface surface;

    private onTextureCreateListener onTextureCreateListener;
    private OnRenderListener onRenderListener;
    public YuvRender(Context context) {
        this.context = context;

        vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        vertexBuffer.put(vertexData).position(0);


        textureBuffer = ByteBuffer.allocateDirect(textureData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        textureBuffer.put(textureData).position(0);
    }

    public void setRenderType(int renderType) {
        this.renderType = renderType;
    }

    public void setOnTextureCreateListener(YuvRender.onTextureCreateListener onTextureCreateListener) {
        this.onTextureCreateListener = onTextureCreateListener;
    }

    public void setOnRenderListener(OnRenderListener onRenderListener) {
        this.onRenderListener = onRenderListener;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        initRenderYuv();
        initRenderMediaCodec();
    }


    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(0.1f, 0.4f, 0.7f, 1.0f);
        if (renderType==RENDER_TYPE_YUV) {
            renderYuv();
        }else if (renderType==RENDER_TYPE_SURFACE){
            renderMediacoec();
        }

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }




    public void setYuvRenderData(int width, int height, byte[] y, byte[] u, byte[] v) {
        this.width_yuv = width;
        this.height_yuv = height;
        this.y = ByteBuffer.wrap(y);
        this.u = ByteBuffer.wrap(u);
        this.v = ByteBuffer.wrap(v);
    }

    private void initRenderMediaCodec() {
        String vertexSource = OpenGLUtil.loadRawShaderSource(context, R.raw.yuv_vertex_shader);
        String fragmentSource = OpenGLUtil.loadRawShaderSource(context, R.raw.surface_fragment_shader);
        program_mediacodec = OpenGLUtil.createProgram(vertexSource, fragmentSource);

        av_Position_mediacodec = GLES20.glGetAttribLocation(program_mediacodec, "av_Position");
        af_Position_mediacodec = GLES20.glGetAttribLocation(program_mediacodec, "af_Position");

        samplerOES_mediacodec = GLES20.glGetUniformLocation(program_mediacodec, "sTexture");

        int[] textureids = new int[1];
        GLES20.glGenTextures(1, textureids, 0);
        textureId_mediacodec = textureids[0];
        Log.e(TAG, "initRenderMediaCodec: textureId"+textureId_mediacodec);

        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId_mediacodec);

        //环绕（超出纹理坐标范围）：（s==x t==y GL_REPEAT 重复）
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
        //过滤（纹理像素映射到坐标点）：（缩小、放大：GL_LINEAR线性）
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

        surfaceTexture = new SurfaceTexture(textureId_mediacodec);
        surface=new Surface(surfaceTexture);
        surfaceTexture.setOnFrameAvailableListener(this);
        if (null!=onTextureCreateListener){
            onTextureCreateListener.onCreate(surface);
        }
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,0);

    }

    private void renderMediacoec() {
//        Log.e(TAG, "renderMediacoec: ");
        surfaceTexture.updateTexImage();
        
        GLES20.glUseProgram(program_mediacodec);
        GLES20.glEnableVertexAttribArray(av_Position_mediacodec);
        GLES20.glVertexAttribPointer(av_Position_mediacodec, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer);

        GLES20.glEnableVertexAttribArray(af_Position_mediacodec);
        GLES20.glVertexAttribPointer(af_Position_mediacodec, 2, GLES20.GL_FLOAT, false, 8, textureBuffer);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,textureId_mediacodec);
        GLES20.glUniform1i(samplerOES_mediacodec,0);

//        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,0);
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
//        Log.e(TAG, "onFrameAvailable: ");
        if (null!=onRenderListener){
            onRenderListener.onRender();
        }
    }


    private void initRenderYuv() {
        String vertexSource = OpenGLUtil.loadRawShaderSource(context, R.raw.yuv_vertex_shader);
        String fragmentSource = OpenGLUtil.loadRawShaderSource(context, R.raw.yuv_fragment_shader);

        programYuv = OpenGLUtil.createProgram(vertexSource, fragmentSource);


        av_Position_yuv = GLES20.glGetAttribLocation(programYuv, "av_Position");
        af_Position_yuv = GLES20.glGetAttribLocation(programYuv, "af_Position");

        sampler_y = GLES20.glGetUniformLocation(programYuv, "sampler_y");
        sampler_u = GLES20.glGetUniformLocation(programYuv, "sampler_u");
        sampler_v = GLES20.glGetUniformLocation(programYuv, "sampler_v");

        textureId_yuv = new int[3];
        GLES20.glGenTextures(textureId_yuv.length, textureId_yuv, 0);

        for (int i = 0; i < textureId_yuv.length; i++) {
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId_yuv[i]);

            //环绕（超出纹理坐标范围）：（s==x t==y GL_REPEAT 重复）
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
            //过滤（纹理像素映射到坐标点）：（缩小、放大：GL_LINEAR线性）
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        }
    }


    private void renderYuv() {
        Log.e(TAG, "renderYuv: ");
        if (
                width_yuv > 0
                        && height_yuv > 0
                        && y != null
                        && u != null
                        && v != null
        ) {


            GLES20.glUseProgram(programYuv);
            GLES20.glEnableVertexAttribArray(av_Position_yuv);
            GLES20.glVertexAttribPointer(av_Position_yuv, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer);

            GLES20.glEnableVertexAttribArray(af_Position_yuv);
            GLES20.glVertexAttribPointer(af_Position_yuv, 2, GLES20.GL_FLOAT, false, 8, textureBuffer);

            //y
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId_yuv[0]);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width_yuv, height_yuv, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, y);

            //u
            GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId_yuv[1]);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width_yuv / 2, height_yuv / 2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, u);

            //v
            GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId_yuv[2]);
            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width_yuv / 2, height_yuv / 2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, v);

            GLES20.glUniform1i(sampler_y, 0);
            GLES20.glUniform1i(sampler_u, 1);
            GLES20.glUniform1i(sampler_v, 2);

            y.clear();
            u.clear();
            v.clear();

            y = null;
            u = null;
            v = null;
        }
    }




    public interface onTextureCreateListener {
        void onCreate(Surface surface);
    }
    public interface OnRenderListener{
        void  onRender();
    }
}
